﻿using System;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace STM32_BootLoader_app更新
{
    public partial class Form1 : Form
    {
        private bool Status;
        private bool isConnected = false;
        private String FilePath = null;


        public Form1()
        {
            InitializeComponent();
        }

        //串口启动完毕
        private void Form1_Load(object sender, EventArgs e)
        {
            comboBox2.SelectedIndex = 8;
            RefreshPortList(comboBox1);
        }

        //查找可用端口添加到ComboBox列表中
        private void RefreshPortList(System.Windows.Forms.ComboBox cmbPort)
        {
            cmbPort.Items.Clear();
            string[] ports = SerialPort.GetPortNames();

            // 使用LINQ对端口进行排序：先按端口名称长度排序，再按字符串排序
            // 这样可以确保COM2排在COM10之前（因为数字部分长度不同）
            var sortedPorts = ports.OrderBy(p => p.Length).ThenBy(p => p).ToArray();

            cmbPort.Items.AddRange(sortedPorts);
            if (cmbPort.Items.Count > 0)
                cmbPort.SelectedIndex = 0;
        }


        //打开与关闭串口
        private void button2_Click(object sender, EventArgs e)
        {
            if (Status)
            {
                button2.Text = "打开串口";
                Status = false;
                panel1.BackColor = Color.Red;
                serialPort.Close();

                isConnected = false;
                BtnSendFile.Enabled = isConnected;
                comboBox1.Enabled = true;
                comboBox2.Enabled = true;
            }
            else
            {
                try
                {
                    serialPort.PortName = comboBox1.Text;                      //端口号
                    serialPort.BaudRate = Convert.ToInt32(comboBox2.Text);     //波特率
                    serialPort.DataBits = 8;     //数据位

                    serialPort.Parity = System.IO.Ports.Parity.None;           //校验位
                    serialPort.StopBits = System.IO.Ports.StopBits.One;        //停止位               
                    serialPort.Handshake = System.IO.Ports.Handshake.None;     //握手协议

                    serialPort.Open();
                    button2.Text = "关闭串口";
                    Status = true;
                    panel1.BackColor = Color.SpringGreen;

                    isConnected = true;
                    comboBox1.Enabled = false;
                    comboBox2.Enabled = false;
                }
                catch (Exception)
                {
                    MessageBox.Show("打开串口失败!", "串调试助手");
                    //throw;
                }

            }
        }

        //打印一行
        private void AppendText(string text)
        {
            Console.Write(text);
            lblStatus.Text = "执行状态：" + text;
        }

        //选择文件
        private void BtnSelectFile_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "Binary Files (*.bin)|*.bin|Hex Files (*.hex)|*.hex|All Files (*.*)|*.*";
                openFileDialog.FilterIndex = 1;

                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    FilePath = openFileDialog.FileName;
                    BtnSendFile.Enabled = isConnected;
                    label3.Text = $"文件大小：{File.ReadAllBytes(FilePath).Length} bytes";
                    txtFilePath.Text = $"文件地址：{FilePath}";
                }
            }
        }
        private async Task SendCommand(string command)
        {
            if (serialPort.IsOpen)
            {
                byte[] data = Encoding.ASCII.GetBytes(command);
                serialPort.Write(data, 0, data.Length);
                await Task.Delay(50); // 短暂延迟确保命令发送完成
            }
        }

        //读串口
        private async Task<bool> WaitForResponse(string expectedResponse, int timeoutMs)
        {
            DateTime startTime = DateTime.Now;
            StringBuilder responseBuilder = new StringBuilder();
            while ((DateTime.Now - startTime).TotalMilliseconds < timeoutMs)
            {
                try
                {
                    if (serialPort.BytesToRead > 0)
                    {
                        AppendText("BootLoader: 2.3\n");
                        string response = serialPort.ReadExisting();
                        responseBuilder.Append(response);

                        string fullResponse = responseBuilder.ToString();

                        // 显示非"Programming completed"的响应
                        if (!fullResponse.Contains("Programming completed") && !string.IsNullOrWhiteSpace(response))
                        {
                            AppendText($"BootLoader: {response.Trim()}");
                        }

                        if (fullResponse.Contains(expectedResponse))
                        {
                            return true;
                        }

                        if (fullResponse.Contains("fail") || fullResponse.Contains("error"))
                        {
                            return false;
                        }
                    }
                    await Task.Delay(50);
                }
                catch
                {
                    return false;
                }
            }
            return false;
        }

        //发送数据等待返回
        private async Task<bool> SendCommandAndWaitResponse(string command, string expectedResponse, int timeoutMs = 2000)
        {
            try
            {
                await SendCommand(command);
                return await WaitForResponse(expectedResponse, timeoutMs);
            }
            catch
            {
                return false;
            }
        }

        //发送bin文件
        private async Task<bool> SendFile(string filePath)
        {
            try
            {
                byte[] fileData = File.ReadAllBytes(filePath);
                long totalBytes = fileData.Length;
                long sentBytes = 0;
                const int blockSize = 256;

                AppendText($"文件大小: {totalBytes} bytes");
                UpdateProgress(0);

                while (sentBytes < totalBytes)
                {
                    int bytesToSend = (int)Math.Min(blockSize, totalBytes - sentBytes);
                    byte[] block = new byte[bytesToSend];
                    Array.Copy(fileData, sentBytes, block, 0, bytesToSend);

                    // 构建命令
                    StringBuilder commandBuilder = new StringBuilder();
                    commandBuilder.Append($"program:{bytesToSend}:");

                    foreach (byte b in block)
                    {
                        commandBuilder.Append(b.ToString("X2"));
                    }
                    commandBuilder.Append("\r");

                    string command = commandBuilder.ToString();

                    // 清空接收缓冲区
                    serialPort.DiscardInBuffer();

                    // 发送命令并等待响应
                    if (!await SendCommandAndWaitResponse(command, "Programming completed", 5000))
                    {
                        return false;
                    }

                    sentBytes += bytesToSend;
                    int percentage = (int)((sentBytes * 100) / totalBytes);
                    UpdateProgress(percentage);

                    // 在UI线程中更新状态
                    this.Invoke((Action)(() =>
                    {
                        label4.Text = $"下载进度: {percentage}% ({sentBytes}/{totalBytes} bytes)";
                        label4.ForeColor = Color.Orange;
                    }));

                    await Task.Delay(10); // 短暂延迟
                }

                UpdateProgress(100);
                UpdateStatus("文件发送完成", Color.Green);
                AppendText("文件传输已完成!");

                return true;
            }
            catch (Exception ex)
            {
                AppendText($"发送文件时出错: {ex.Message}");
                return false;
            }
        }

        private void UpdateStatus(string message, Color color)
        {
            if (this.InvokeRequired)
            {
                this.Invoke((Action)(() => UpdateStatus(message, color)));
                return;
            }

            lblStatus.Text = message;
            lblStatus.ForeColor = color;
        }

        //更新进度条
        private void UpdateProgress(int percentage)
        {
            if (this.InvokeRequired)
            {
                this.Invoke((Action)(() => UpdateProgress(percentage)));
                return;
            }

            progressBar.Value = Math.Max(0, Math.Min(100, percentage));
        }

        //下载应用
        private async Task SendFileToDevice()
        {
            // 1. 验证通信
            AppendText("3：正在验证通信..\n.");
            if (!await SendCommandAndWaitResponse("hello\r", "Hello back"))
            {
                AppendText("4：通信验证失败。\n");
                return;
            }

            // 2. 擦除应用程序区域
            AppendText("4：正在擦除应用程序区域...");
            if (!await SendCommandAndWaitResponse("erase\r", "Erase completed", 10000))
            {
                AppendText("5：擦除失败或超时");
                return;
            }
            AppendText("5：擦除成功完成");

            // 3. 发送文件
            AppendText("6：正在发送应用程序...");
            if (await SendFile(FilePath))
            {
                AppendText("7：应用程序发送成功。");

                // 4. 跳转到应用程序
                AppendText("8：正在跳转到应用程序...");
                await SendCommand("jump\r");
            }
            else
            {
                AppendText("7：应用程序发送失败");
            }
        }

        //下载应用按钮事件
        private async void BtnSendFile_Click(object sender, EventArgs e)
        {
            if (!isConnected)
            {
                MessageBox.Show("请先连接设备", "错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (string.IsNullOrEmpty(FilePath) || !File.Exists(FilePath))
            {
                MessageBox.Show("请选择有效的文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            BtnSendFile.Enabled = false;
            try
            {
                await SendFileToDevice();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送文件失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                AppendText($"发送文件失败: {ex.Message}");
            }
            finally
            {
                BtnSendFile.Enabled = isConnected;
            }
        }
    }
}
